package com.majun.config;

import com.majun.util.WebSocketUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

/**
 * <p> Description: websocket 配置类 </p>
 * <p> Title: WebSocketConfig </p>
 * <p> Create Time: 2019/7/28 15:51 </p>
 * <a href="https://blog.csdn.net/PacosonSWJTU/article/details/51914567">参见</a>
 *
 * @author zhongzhipeng
 * @since 1.0
 */
@Slf4j
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/groupChat", "/p2pChat").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic", "/user");
        registry.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(new ClientInboundChannelInterceptor());
    }

    /**
     * 客户端消息拦截器,该拦截器会拦截到客户端 建立连接、发送消息、销毁连接等操作
     */
    private static class ClientInboundChannelInterceptor implements ChannelInterceptor {

        /**
         * 接收到客户端发送消息之前回调该方法 ***重要*** 这里可以做websocket权限认证,暂时只做统计在线连接
         *
         * @param message 客户端发送的消息
         * @param channel 传递消息的通道
         * @return Message客户端发送的消息。注返回值为 null 则会拦截后续方法调用结束消息传递。后期做权限认证重要依据
         */
        @Override
        public Message<?> preSend(Message<?> message, MessageChannel channel) {
            // 将原始消息头转为 Stomp 消息头
            StompHeaderAccessor stompHeaderAccessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
            log.info(" stompHeaderAccessor = {}", stompHeaderAccessor);
            if (stompHeaderAccessor == null || stompHeaderAccessor.getCommand() == null) {
                // 不存在消息头这种情况基本不存在,stomp协议每次发送消息都会固定传递几个头信息,为了下面方法不抛出警告做下判断
                log.info("消息头为空,消息被拦截,请检查是否基于 stomp 协议传递消息,message = {} , channel = {}", message, channel);
                return null;
            }

            switch (stompHeaderAccessor.getCommand()) {
                case CONNECT:
                    stompConnectHandle();
                    break;

                case DISCONNECT:
                    stompDisConnectHandle();
                    break;

                default:
                    break;
            }
            return message;
        }

        /** stomp 连接处理 */
        private void stompConnectHandle() {
            int onlineConnect = WebSocketUtil.getOnlineConnectCount().incrementAndGet();
            log.info("stomp 建立连接 destination = {} ", onlineConnect);
        }

        /** stomp 连接断开处理 */
        private void stompDisConnectHandle() {
            int onlineConnect = WebSocketUtil.getOnlineConnectCount().decrementAndGet();
            log.info("stomp 断开连接  当前连接数 onlineConnect = {}", Math.max(onlineConnect, 0));
        }
    }
}
